#pragma once
#include <atomic>
#include <thread>
#include <functional>
#include <iostream>
#include <chrono>
#include "Data/Model_Base.h"
#include "Media/Decoder/AudioDecoder.h"
#include "Media/Decoder/VideoDecoder.h" 
#include "Media/Decoder/BaseDecoder.h"
#include "Data/AVPacket/AVPacketManager.h"


template<typename T>
class DecoderThread {

private:
    std::shared_ptr<T>& decoder_ptr; // audio and video decoder pointer
    AVPacketManager& queue_ptr;  // audio and video queue pointer
    std::function<void()> init_func; // init function

protected:    
    std::atomic<bool> thread_running;  // Use to control thread's running status
    std::unique_ptr<std::thread> thread_ptr;
    
public:
    DecoderThread(std::shared_ptr<T>& decoder_ptr, AVPacketManager& queue_ptr, std::function<void()> init_func)
        : decoder_ptr(decoder_ptr), queue_ptr(queue_ptr), init_func(init_func), thread_running(false) {}


    ~DecoderThread() {
        stop();
    }

    void run() {
        int error_count = 0;
        try {
            this->init_func();
            while(thread_running){
                std::cout << this->queue_ptr.get_type_name() << ": decoder thread wait for sem_decoder" << std::endl;
                AVPacketManager& queue = queue_ptr;
                while (thread_running) {
                    AVPacket packet;
                    if (queue.get(&packet,std::chrono::milliseconds(2000)) == 0) {
                        if(!decoder_ptr->decode(&packet)){
                            ++error_count;
                            std::cerr << "Decoding error occurred: " << error_count << std::endl;  // Print error info
                            if(error_count > 3){
                                break;  // Skip the current round instead of throwing an exception
                            }
                        }
                        av_packet_unref(&packet);
                    } else {
                        std::this_thread::sleep_for(std::chrono::milliseconds(10));
                    }
                }
            }
            std::cout << this->queue_ptr.get_type_name() << ": exit decoder thread" << std::endl;
        } catch (const std::exception& e) {
            std::cerr << "Exception in DecoderThread: " << e.what() << std::endl;
            thread_running = false;
        }
    }

    void start() {
        thread_running = true;
        if (thread_ptr && thread_ptr->joinable()) {
            throw std::runtime_error("Thread is already running!");
        }
        thread_ptr = std::make_unique<std::thread>(&DecoderThread::run, this);
    }

    void stop() {
        thread_running = false;
        if (thread_ptr && thread_ptr->joinable()) {
            thread_ptr->join();
            //thread_ptr.reset();
        }
    }

    void reset() {
        stop();
        thread_running = true;
        start();
    }
};
